/**
 * @brief 设备管理接口。这个接口是SOCKET 0 接口，用来和设备管理服务器进行交互上报设备运行状态、进行远程升级
 *   以及设备设置
 * @version 1.0
 * @date 2019-1-2
 * @author 宋炜
 */
#ifndef __DEV_MANAGE_HPP__
#define __DEV_MANAGE_HPP__
#include <stdlib.h>
#include <malloc.h>

#include <string>
#include <functional>
#include <atomic>

#include "sys_var.hpp"
#include "uvlper.hpp"
#include "timer.hpp"
#include "ary_str.hpp"
#include "filterio.h"
#include "resolver.hpp"
#include "traits.hpp"
#include "misc.h"

class devManage : public filterIO
{
public:
	enum err_code{
		OK,
		ERR_ALLOC_MEM,
		ERR_SHUTDOWN,
		ERR_NULL_DATA,
		ERR_GET_SN,
		ERR_EMPTY_DATA,
		ERR_NULL_POINTER,
		ERR_SN_CONVERT,
		ERR_DATA_SIZE,
		ERR_OUT_TLV_RANGE,
		ERR_NULL_DTU,
		ERR_ICCID_CONVERT,
		ERR_IMEI_CONVERT
	};
	enum optTag{
	//       名称              	  值                 描述  
	// ******************************************************************************************
		TAG_HEART   		= 0X00,		// 心跳包，心跳包长度为0, 无内容字段
		TAG_SITE   		= 0XC0,		// 站点
		TAG_TEL_No 		= 0xc1,		// 电话号码
		TAG_FIRMWARE 		= 0XC2,		// 固𢓐版本
		TAG_ERR_CODE 		= 0XC3,		// 错误码
		TAG_RUNNING_TIME 	= 0XC4,		// 运行时间
		TAG_VOLTAGE 		= 0XC5,		// 电压
		TAG_UPGRADE 		= 0XE7,		// 升级
		TAG_REMOTE_SET 		= 0XE6,		// 
		TAG_UPGRADE_DATA	= 0XC8,		//
		TAG_CURRENT_TIME    	= 0XC9,		//	
		TAG_SIGNAL          	= 0XCA,		//
		TAG_TEMP            	= 0XCB,		//
		TAG_SOLAR_VOLTAGE	= 0XCE,		//
		TAG_SOLAR_CURRENT 	= 0XCD,		//
		TAG_ICCID		= 0XD3,		//
		TAG_IMEI		= 0XD4,		//
	};
	/**
	 * 设备和服务器之间的交互状态
	 */
	struct comStatus{
		enum enum_com_status{
			STANDBY,                // 等待连接
			READY,			// 就绪
			REQED,                  // 向服务器发送了请求
			RUNING,                 // 正在执行任务
			SVR_RESPED,             // 服务器已经反馈
			CMD_RECVED,             // 收到服务器指令
			DEV_RESPED              // 设备已经反馈
		};
	};
	struct upgradeStatus{
		enum upgrade_status{
			STANDBY,
			READY,
			REQ ,                   // 向服务器请求
			DOWNLOAD,               // 下载文件
			INSTALL,                // 执行安装脚本
			REBOOT,                 // 设备重启
			CANCEL,                 // 取消升级
			FINISH                  // 升级完成
		};
	};
	struct remoteSetting{
		enum remote_setting{
			STANDBY,
			READY,
			RECVED,
			RUNING,
			RESPED
		};
	};
	struct statusReport{
		enum status_report{
			STANDBY,
			READY,
			REPORTED,
			REPORT_ACK
		};
	};
	/*
	* 主状态描述 
	*/
	struct mainStatus{
		enum main_status{
			STANDBY,               // 没有连接
			READY,
			REMOTE_SET,
			REPORT,
			UPGRADE
		}; 
	};
	/**
	 * 连接指令数据
	 */
	struct stConnectCmd{
		struct addrinfo         * head;                    // 解析链表头 
		struct addrinfo         * current;                 // 当前结点
		devManage               * obj;                     // 对象指针
		uv_connect_t            * req; 
		int                       status;                  // 连接处理结果

		stConnectCmd()
		{
			head = nullptr;
			current = nullptr;
			obj = nullptr;
			req = nullptr;
		}

		virtual ~stConnectCmd()
		{
			if( head ) uv_freeaddrinfo( head );            
			if( req ) free( req );

			head = nullptr;
			current = nullptr;
			obj = nullptr;
			req = nullptr;
		}

		stConnectCmd( const stConnectCmd & b )
		{
			head = b.head;
			current = b.current;
			obj = b.obj;
			req = b.req;
			status = b.status;
		}

		stConnectCmd& operator=( const stConnectCmd& b )
		{
			head = b.head;
			current = b.current;
			obj = b.obj;
			req = b.req;
			status = b.status;
			return *this;
		}
	};

	struct stWriteCmd{
		uv_write_t      * req;
		uv_buf_t          buf;
		devManage       * obj;

		stWriteCmd()
		{
			req = nullptr;
			buf.base = nullptr;
			buf.len = 0;
			obj = nullptr;
		}

		virtual ~stWriteCmd()
		{
			if( req ) free( req );
			if( buf.base ) free( buf.base );
			req = nullptr;
			buf.base = nullptr;
			buf.len = 0;
			obj = nullptr;
		}

		stWriteCmd( const stWriteCmd& b )
		{
			req = b.req;
			if( buf.len < b.buf.len ){
				if( buf.base == nullptr ){
					buf.base = ( char *)malloc( b.buf.len );
				}else{
					buf.base = ( char *)realloc( buf.base , b.buf.len );
				}

				if( buf.base ){
					buf.len = b.buf.len;
					memcpy( buf.base , b.buf.base , b.buf.len );
				}else{
					throw ERR_ALLOC_MEM;
				}
			}else{
				memcpy( buf.base , b.buf.base , b.buf.len );
			}
		}

		stWriteCmd& operator=( const stWriteCmd& b )
		{
			req = b.req;
			if( buf.len < b.buf.len ){
				if( buf.base == nullptr ){
					buf.base = ( char *)malloc( b.buf.len );
				}else{
					buf.base = ( char *)realloc( buf.base , b.buf.len );
				}

				if( buf.base ){
					buf.len = b.buf.len;
					memcpy( buf.base , b.buf.base , b.buf.len );
				}else{
					throw ERR_ALLOC_MEM;
				}
			}else{
				memcpy( buf.base , b.buf.base , b.buf.len );
			}

			return *this;
		}
	};
	/**
	 * 网络关闭命令
	 */
	struct stCloseCmd{
		uv_shutdown_t   * req;
		devManage       * obj;
	};
	/**
	 */
	struct stCmdResp{

	};
	/**
	 * TLV指令
	 */
	struct stTLV{
		uint8_t     tag;                // 操作指令
		uint8_t     len;                // 指令长度
		uint8_t     data[ 255 ];        // 具体正文
	}__attribute__((packed));
	/**
	 * 设备上行数据包
	 */
	const static int PACKET_DATA_POS = 11;	// 数据包中的数据位置
	const static int PACKET_FIX_LEN = 13;
	struct stPacket{
		uint8_t     head;               // 设备上行数据包头
		char        sn[ 8 ];            // 设备SN
		uint16_t    len;                // 数据包内容长度
		stTLV     * data;               // 数据包内容
		uint8_t     crc;                //
		uint8_t     tail;               // 数据包尾

		int         packet_count;       // TLV 数据包数量，这个值不参与到通讯中
		int         current_count;      // 
		/**
		 */
		stPacket()
		{
			data = nullptr;
			head = 0x5a;
			tail = 0xa5;
			len = 0;
			packet_count = 0;
			current_count = 0;
		}
		/**
		 * @brief 
		 * @param count
		 */
		stPacket( int count )
		{
			packet_count = count;
			current_count = 0;
			len = 0;
			head = 0x5a;
			tail = 0xa5;

			data = ( stTLV *)malloc( sizeof( stTLV ) * count );
			if( data == nullptr ) throw ERR_ALLOC_MEM;

			std::shared_ptr< CSysVar > ptvar = GetOrCreateSysVar();
			if( ptvar ){
				std::string  str;
				
				ptvar->GetValue( "/SN" , str );
				size_t len = str.length();
				len = ( len >> 1 ) + ( len & 0x1 );
				uint8_t * p = ( uint8_t *)malloc( str.length() );
				if( p ){
					bool ret = str2hex4bit( str , p );
					memcpy( sn , p , len );
					free( p );
					if( ret == false ) throw ERR_SN_CONVERT;
				}else{
					throw ERR_ALLOC_MEM;
				}
			}else{ throw ERR_GET_SN;}
		}

        	~stPacket(){
			if( data ) free( data );
			data = nullptr;
		}
    	}__attribute__((packed));

	typedef stPacket stUpPacket;			// 上行包
	typedef stPacket stDownPacket;			// 下行包

	struct stFUNID{
		enum funID{
			UPGRADE_DOWNLOAD,
			UPGRADE_REQ,
			UPGRADE_INSTALL,
			REMOTE_SETTING,
			REPORT_START,
			MAKE_UPPACKET,
			HEART
		};
	};

	long CONN_KP_TIME  = 60000;                      		// 数据操作完后连接保持一分钟
	long RECONN_TIME   = 60000 * 60;                 		// 一个小时上线一次
	long CONN_OVERTIME = 5000;                       		// 连接超时时间
	long COM_OVERTIME  = 5000;                       		// 通讯超时间
	long REPORT_TIME   = 60000 * 60 * 12;            		// 12个小时作一次工况汇报
	long HEART_TIME    = 10000;                      		// 心跳
private:
	time_t                   m_time_start;			 	// 运行启始时间 ， 模块起动时𥘉始化
	std::atomic< bool >      m_ntp_is_sync;          		// 判断ntp是否进行了同步

	std::string              m_server;               		// 服务器地址
	int                      m_port;                 		// 服务器端口
	std::string              m_sn;			 		// 设备SN
	std::string              m_imei;		 		// 设备IMEI
	std::string              m_iccid;		 		// sim iccid
	std::string		 m_firm_add;				// 固件服务器地址
	int			 m_firm_port;				// 固件下载端口号码

	std::atomic< time_t >    m_run_time; 				//
	
	std::atomic< bool >      m_connected;                           // 维护TCP是否连接的状态
	std::atomic< bool >      m_is_destory;                          // 是否正在销毁对象

	std::atomic< comStatus::enum_com_status >      m_com_status;
	std::atomic< mainStatus :: main_status >       m_main_status;
	std::atomic< upgradeStatus::upgrade_status >   m_upgrade_status;              // 升级状态机
	std::atomic< statusReport::status_report >     m_status_report;               // 工况汇报状态机
	std::atomic< remoteSetting::remote_setting >   m_remote_setting_status;       // 远程设置状态机

	//CTimer                   m_conn_kp_time;                      // 连接保持时间
	CTimer                   m_reconn_timer;                      // 重连时间
	CTimer                   m_conn_overtime;                     // 连接超时时间
	CTimer                   m_com_overtime;                      // 通讯超时时间
	CTimer                   m_report_timer;                      // 工况汇报计时器
	CTimer                   m_report_try_timer;                  // 如果工况汇报的时候通讯忙，等待一会
	CTimer                   m_heart_timer;                       // 
	
	uv_tcp_t                 m_tcp_handle;                        // TCP 句柄

	std::shared_ptr< char >  m_buf;                               // 数据缓冲区
	std::mutex		 m_mutex;
	/// --------------------------------------------------------------------------------------------
	/// 
	std::string  		 m_upgrade_pswd;                      // 固𢓐升级密码，在协商过程中或取。

	resolver               * p_res;				      // 域名解析器
private:
	/**
	 * @brief 
	 */
	bool parse_tlv( const char * data , size_t len , stDownPacket& pkg );
	/**
	 * @brief 𥘉始化上行数据包。
	 * @param { OUT } packet ， 要𥘉始化的数据包
	 * @param { IN } count , 要𥘉始化的TLV数量
	 * @return 成功返回true，否则返回false
	 * @exception 
	 *     内存分配失败：ERR_ALLOC_MEM
	 */
	bool initUpPacket( stUpPacket * packet , int count = 1 );
	/**
	 * @brief 在上行包中𣸸加标签
	 * @param packet 目标数据包
	 * @param tag , 操作标签
	 * @param len , 数据长度
	 * @param v ， 目标数据
	 */
	template< typename TYPE , typename TYPE_TRAITS = TraitsHelper< TYPE > >
	void addUpTag( stUpPacket * packet , optTag tag , int len , TYPE v )
	{
		if( packet == nullptr ) return;
		
		if( packet->current_count < packet->packet_count ){
			packet->data[ packet->current_count ].tag = tag;
			packet->data[ packet->current_count ].len = len * sizeof( typename TYPE_TRAITS::value_type );
			// 指针则直接COPY
			if( TYPE_TRAITS::isPointer == true ){
				memcpy( packet->data[ packet->current_count ].data , v , len * sizeof( typename TYPE_TRAITS::value_type )); 
				packet->len = packet->len + len * sizeof( typename TYPE_TRAITS::value_type ) + 2;
			}else{// 值则取地址COPY
				memcpy( packet->data[ packet->current_count ].data , &v , len * sizeof( typename TYPE_TRAITS::value_type ) );
				packet->len = packet->len + sizeof( typename TYPE_TRAITS::value_type ) + 2;
			}

			packet->current_count ++;

			packet->crc = crc8( packet );
		}else{
			throw ERR_OUT_TLV_RANGE;
		}
	}
	/**
	 * @brief 释放连接用的地址链
	 * @param cmd , struct stConnectCmd
	 */
	void freeConnCmd( struct stConnectCmd * cmd );
	/**
	 * @brief 构造连接指令链
	 * @param info
	 * @return 成功返回指令指针，否则返回nullptr
	 */
	struct stConnectCmd * initConnCmd( struct addrinfo * info );
	/**
	 * @brief 发送数据给服务器
	 * @param data
	 * @param len
	 * @return 成功发送提交返回true，否则返回false
	 * @note 这个函数成功后并不意味之成功将数据发送出去，而是成功将数据提交给libuv，是否成功
	 *    在回调中根据状态判断
	 */
	bool send( const char * data , size_t len );
	/**
	 * @brief 𥘉始化状态
	 */
	void init_status();
	/**
	 * @brief 进行版本协商，完成后调用fun执行升级
	 * @param ver
	 * @param rst_ver , 协商后的版本号
	 */
	void negotiate( const std::string& ver , std::string& rst_ver );
	/**
	 * @brief 初始化TCP连接
	 * @param server 服务器地址，可以是域名也可以是IP地址
	 * @param port 服务器端
	 */
	void initTCP( const std::string& server , int port );
	/**
	 * @brief 执行连接操作
	 * @param add , 经过DNS解析后的地址
	 */
	void do_connect( struct stConnectCmd * cmd1 , struct addrinfo * add );
	/**
	 * @brief 解析协议.
	 * @param 【 IN 】data , 接收到的数据
	 * @param 【 IN 】len ， 接收到的数据长度
	 * @param 【 IN 】rst ， 协议解析整理的结果
	 * @return 成功返回true，否则返回false
	 */
	bool parse( const char * data , size_t len , stDownPacket & rst );

	virtual ~devManage();
	/**
	 * @brief 执行服务器登录
	 */
	void do_login();
	/**
	 * @brief 请求升级内容，比如文件路径
	 */
	void do_query_upgrade( const std::string& ver );
	/**
	 * @brief 下载失败
	 */
	void failDownload();
	/**
	 * @brief 完成安装
	 */
	void finishInstall();
	/**
	 * @brief 开始下载
	 */
	void startDownload();
	/**
	 * @brief 下载文件
	 * @param file
	 */
	void do_download( const std::string& file );
	/**
	 * @brief 执行安装操作
	 */
	void do_install();
	/**
	 * @brief 𥘉始化计时器
	 */
	void initTimer();
	/**
	 * @brief 处理远程设置数据发送后的状态转换
	 * @param status , 通讯状态
	*/
	void do_on_write_remote_set( int status );
	/**
	 * @brief 处理状态上报的状态转换
	*/
	void do_on_write_status_report( int status );
	/**
	 * @brief 处理升级数据发送后的状态转换处理
	*/
	void do_on_write_upgrade( int status );
	/**
	 * @brief 主状态转换处理
	*/
	void do_on_write_main( int status );
	/**
	 * @brief 计算CRC8
	 * @param data 要计算的数据
	 * @param len 数据长度
	 * @return 计算成功后返回计算结果
	 * @exception 如果数据指针空，拋出ERR_NULL_DATA
	 */
	uint8_t makeCRC8( const char * data , size_t len );
	/**
	 * @brief 检验下行包CRC是否正确
	 * @param data
	 * @param len
	 * @return 正确返回true, 否则返回false
	 */
	bool crc8( const char * data , size_t len );
	/**
	 * @brief 生成CRC8记录
	 * @param packet
	 * @return CRC8的计算结果
	 * @exception 操作失败拋出 ERR_MAKE_CRC
	 */
	uint8_t crc8( const stUpPacket * packet );
	/**
	 * @brief 生成平坦包
	 * @param 结构化的数据包
	 * @exception 
	 *  ERR_ALLOC_MEM 
	 * @note 指针必须在外部释放。
	 */
	char * to_raw( const stUpPacket * packet );
	/**
	 * @brief 解析下行数据TLV部分
	 * @param {IN} data , 要解析平坦数据
	 * @param {IN} len , 要解析的数据长度
	 * @param {OUT} rst , 解析的结果
	 * @return  操作成功返回true , 否则返回false
	 */
	bool parse_tlv( const char * data , size_t len , stTLV** rst );
	/**
	 * @brief 计算TLV数据包数量
	 * @param {IN} data ,  要解析的数据
	 * @param {IN} len , 数据长度
	 * @return  成功返回数量，否则返回0
	 */
	size_t caculate_down_packet_count( const char * data , size_t len );
	/**
	 * @brief 判断是否是响应包
	 */
	bool is_resp( const stDownPacket& packet );
	/**
	 * @brief 派发指令
	 * @param packet
	 */
	void dispatch_cmd( const stDownPacket & packet );
	/**
	 * @brief 派发服务器响应
	 * @param packet
	 */
	void dispatch_resp( const stDownPacket & packet );
	//
	bool on_forward( size_t len , const char * date , err_code &err , size_t& rlen , char ** rd );
	bool on_backward( size_t len , const char * data , err_code &err , size_t& rlen , char ** rd  );
	// 以下是状态转换操作
	/**
	 * @brief 连接断开后处理主状态变化
	 */
	void on_disconnect_of_main_status();
	/**
	 * @brief 建立连接后处理主状态变化
	 */
	void on_connected_of_main_status();
	/**
	 * @brief 开始进行远程设置
	 */
	void start_setting();
	/**
	 * @brief 完成远程设置
	 */
	void finish_setting( const std::string& str );
	/**
	 * @brief 开始进行工况汇报
	 */
	void start_report();
	/**
	 * @brief 完成工况汇报
	 */
	void finish_report();
	/**
	 * @brief 开始更新
	 */
	void start_upgrade();
	/**
	 * @brief 完成更新
	 */
	void finish_upgrade();
	/**
	 * @brief 处理错误信息
	 * @param e 错误代码
	 */
	void do_process_error( stFUNID::funID id , err_code e );
	/**
	 * 处理升级请求返馈
	 * @return 如果是自己的任务，返回true; 否则返回false
	 */
	bool process_upgrade_req_resp( const std::string& resp );
	bool process_upgrade_start_resp( const std::string& resp );
	void process_upgrade_negtiation( size_t len , const char * data );
	bool process_upgrade_download_resp( const std::string& resp );
	
	void process_report_resp( size_t len , const char * data );

	void process_reg_resp( size_t len , const char * data );
	/**
	 * @brief 等待服务器接受连接
	 * @param overtime , 连接超时时间
	 */
	void wait_accept( long overtime );
	/**
	 * @brief 发送心
	 */
	void heart_beat();
	/**
	 * @brief 构造升级请求包
	 */
	bool makeUpgradePacket( const std::string& ver , stUpPacket& packet );
	/**
	 * @brief 准备IMEI
	 */
	size_t papreIMEI( uint8_t * rst );
	/**
	 * @brief 准备ICCID汇报
	 */
	size_t papreICCID( uint8_t * rst );
	/**
	 * @brief 准备信号强度汇报
	 */
	size_t papreSignal( uint8_t * rst );
	/**
	 * @brief 准备错误代码汇报
	 */
	size_t papreErrorCode( uint8_t * rst );
	/**
	 * @brief 准备运行时间汇报
	 */
	size_t papreRunningTime( uint8_t * rst );
public:
	/**
	 * @brief 模块构造
	 * @param {IN} server , 服务器地址。可以是域名也可以是IP地址
	 * @param {IN} port , 服务器端口
	 */
	devManage( const std::string& server , int port );
	/**
	 * @brief 执行升级
	 * @param ver , 
	 */
	void RunUpgrade( const std::string& ver );
	/**
	 * @brief 断开连接
	 */
	void Disconnect();
	/**
	 * @brief 销毁对象。这个类必须显式调用销毁。
	 */
	void Destory();
	/**
	 * @brief 关闭连接
	 * @exceptions: 如果内存分配失败，ERR_ALLOC_MEM
	 */
	void Close();
	/**
	 * @brief 处理接收数据
	 * @param data 接收的数据
	 * @param len 数据的长度
	 */
	void OnRecv( const char * data , size_t len );
	/**
	 * @brief 连接通知处理
	 * @param {IN} on , 连接通知
	 */
	void OnConnect( struct stConnectCmd * on);
	/**
	 * @brief 处理网络错误
	 * @param {IN} e , 错误编号
	 */
	void OnNetError( int e );
	/**
	 * @brief 发送后回调
	 * @param {IN} status ， 发送结果
	 */
	void OnWrite( int status );
	/**
	 * @brief 关闭发送通道，并等待所有的发送操作完成
	 * @param status
	*/
	void OnShutdown( int status );
	/**
	 * @brief 关闭TCP连接
	*/
	void OnClose( int status );
	/**
	 * 
	 */
	char * GetBuffer(){ return m_buf.get(); }
	/**
	 * @brief 控制设备进行时钟同步
	 */
	void timeSync();
};

extern devManage * GetOrCreateDevManager();
#endif
